package pb

import (
	"encoding/hex"
	"errors"
	"fmt"

	"github.com/golang/protobuf/proto"
	"hundsun.com/hsl/hschain/common/crypto"
	"hundsun.com/hsl/hschain/common/log/logging"
	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/protos"
	"hundsun.com/hsl/hschain/protos/ledger"
)

// GetBlockLog 根据指定的日志对象派生一个Block相关的日志对象
func GetBlockLog(log logging.Log, block *ledger.Block) logging.Log {
	if block == nil || block.Header == nil {
		return log
	}
	logFields := make(map[string]interface{})
	logFields[logging.LogFieldBlockNumber] = block.Header.Number
	if block.Header.BlockSequence > 0 {
		logFields[logging.LogFieldBlockSequence] = block.Header.BlockSequence
	}
	if log.IsTraceEnabled() {
		logFields[logging.LogFieldBlockHash] = block.Hash().GetShortString()
	}
	return log.WithFields(logFields)
}

// ValidateBasicInfoOfBlock 验证区块基本信息（未背书和已背书的块通用），验证内容如下：
// 1. 区块头各字段格式（不含共识字段和序列号字段）
// 2. 块中必须有交易
// 注意：此方法不适用创世块的校验！
func ValidateBasicInfoOfBlock(block *ledger.Block, minTxFee uint64, reward uint64) error {
	//触发初始化
	//_ = GetGenesisBlock()

	if block.Header == nil {
		return types.ErrNoBlockHeader
	}

	if block.Header.Number == 0 {
		return types.ErrNonPositiveBlockNumber
	}

	hashSize, err := crypto.HashSize()
	if err != nil {
		return err
	}
	if len(block.Header.ParentHash) != hashSize {
		return types.ErrInvalidParentHashSize
	}

	if block.Header.BlockTime < genesisBlock.Header.BlockTime {
		return types.ErrBlockTimeBeforeGenesisBlock
	}

	// 判断区块是否包含挖矿交易
	if minTxFee != 0 {
		if len(block.Transactions) == 0 {
			return types.ErrEmpty
		}
		miningTx := block.Transactions[0]
		if miningTx.Ty != "nativeReward" {
			return types.ErrInvalidTxType
		}
		if miningTx.GetValue() != reward {
			return types.ErrInvalidParam
		}
	}

	checkBlockNumber, checkBlockEnabled := GetCheckBlockNumber(block.Header.Number)
	if checkBlockEnabled && checkBlockNumber != block.Header.CheckBlock {
		return types.ErrInvalidCheckBlockNumber
	}
	if checkBlockEnabled && len(block.Header.StateHashOfCheckBlock) != hashSize {
		return types.ErrInvalidCheckBlockStateHashSize
	}

	if block.Header.Signature == nil || len(block.Header.Signature.Signature) == 0 || len(block.Header.Signature.PublicKey) == 0 {
		return types.ErrInvalidBlockSignatureFormat
	}

	if len(block.Transactions) == 0 {
		return types.ErrNoTransactions
	}

	return nil
}

var (
	errFmtBlockNumberDiscontinuous      = "block number discontinuous, expected:%d, actual:%d"
	errBlockTimeNotGreaterThanPrevBlock = errors.New("block time must be greater than previous block")
)

// ValidatePrevBlockInfoOfBlock 校验上一块有关信息：区块号连续、block.BlockTime>lastBlkTime、parentHash
func ValidatePrevBlockInfoOfBlock(currentBlock *ledger.Block, lastBlockTime int64, lastBlockNumber uint64, lastBlockHash protos.Hash) error {
	if currentBlock.Header.Number != lastBlockNumber+1 {
		return fmt.Errorf(errFmtBlockNumberDiscontinuous, lastBlockNumber+1, currentBlock.Header.Number)
	}
	if currentBlock.Header.BlockTime <= lastBlockTime {
		return errBlockTimeNotGreaterThanPrevBlock
	}
	if !lastBlockHash.Equals(currentBlock.Header.ParentHash) {
		return fmt.Errorf("parentHash error,expected:%s,actual:%s", hex.EncodeToString(lastBlockHash), hex.EncodeToString(currentBlock.Header.ParentHash))
	}
	return nil
}

var (
	errTxTTLExceedLimit = errors.New("ttl of tx exceed limit")
	errTxExpiredFmt     = "tx expired,blockTime:%d,txTime:%d"
)

// ValidateTxExpiration 检查交易过期时间，包含如下检查项
// 1）TTL ：必须不大于创世块中配置的值
// 2）|createTime+TTL-blockTime|<=创世块中允许的时间误差
func ValidateTxExpiration(blockTime int64, tx *ledger.Transaction) error {
	if tx == nil {
		return errors.New("nil tx")
	}
	timeLimit := GetGenesisTransactionPayload().GetTimeLimit()
	if tx.TTL > timeLimit.MaxTxTTL {
		return errTxTTLExceedLimit
	}
	minTxValidTime := tx.CreateTime - 1000*int64(timeLimit.MaxTimeIntervalError)
	maxTxValidTime := tx.CreateTime + 1000*int64(timeLimit.MaxTimeIntervalError) + 1000*int64(tx.TTL)
	if blockTime < minTxValidTime || blockTime > maxTxValidTime {
		return fmt.Errorf(errTxExpiredFmt, blockTime, tx.CreateTime)
	}

	return nil
}

var errTxSizeExceedLimit = errors.New("size of tx exceed limit")

// ValidateTxSize 检查交易大小
func ValidateTxSize(tx *ledger.Transaction) (int, error) {
	if tx == nil {
		return 0, errors.New("nil tx")
	}
	txBytes, err := proto.Marshal(tx)
	if err != nil {
		return 0, err
	}
	blockLimit := GetGenesisTransactionPayload().BlockLimit
	if len(txBytes) > int(blockLimit.MaxTxSizeBytes) {
		return 0, errTxSizeExceedLimit
	}
	return len(txBytes), nil
}

// GetTxHashesInBlock 获取块中的哈希
func GetTxHashesInBlock(block *ledger.Block) []protos.Hash {
	txHashes := make([]protos.Hash, len(block.Transactions))
	for i, tx := range block.Transactions {
		txHashes[i] = tx.Hash()
	}
	return txHashes
}
